Guild icon
Project Sekai
🔒 CrewCTF 2023 / ❌-rev-pv-pro
Avatar
pv pro - 1000 points
Category: Rev Description: stage 0_0 Author : cirn09 Files: No files. Tags: No tags.
Sutx pinned a message to this channel. 07/07/2023 10:02 PM
Avatar
@Surg wants to collaborate 🤝
02:31
@Violin wants to collaborate 🤝
Avatar
@snwo wants to collaborate 🤝
Avatar
@Iyed wants to collaborate 🤝
Avatar
@Legoclones wants to collaborate 🤝
Avatar
idk if anyone has been looking at this
10:12
but ive started
10:15
program grabs 64 bytes from user into a global buffer, copies the first 7 to a second buffer gets the current timestamp 💀 and then one massive checker function after the rev function it writes from another global buffer 6 bytes then a newline. This prints wrong! when its wrong, but it looks like it generates that string from the checker function, rather than it being hardcoded
10:15
im going to start to dig into the main target after i eat...
Avatar
could we maybe optimize it more
10:34
to get a more clear version of it
10:35
we could use mcsema and LLVM
10:36
but I guess I'm going to try to take decompiled code and compile it manually with optimization flag
Avatar
yeah ill take a stab at that route
10:43
time for the hard part
10:44
#include <time.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> char inputbuf[64] = {0}; char inputbuf2[7] = {0}; char outputbuf[6] = {0}; char outputbuf2[2] = {0x0a, 0x00}; time_t t; void getUserInput(char* input, int size){ for (int i = 0; i < size; i++){ char x; read(0, &x, 1); if (x == 0xa){ break; } input[i] = x; } } void rework(){ } int main(){ getUserInput(inputbuf, 64); memcpy(inputbuf2, inputbuf, 7); t = time(NULL); rework(); write(1, outputbuf, 6); write(1, outputbuf2, 2); }
Avatar
i did a sanity check for timing attack
11:28
no luck
Avatar
get thebadgod in
Avatar
im hoping that making significant guesses on some stack variables doesn't ruin O3 optimization
Avatar
I don't think it matters much
14:43
since we are going to analyze that statically
14:43
we're not interested in running it
Avatar
I hope it gives close results
Avatar
theres a 512b struct thats accessed like 2 shorts and a large char array
14:44
that pretty much resolved all of the void types i was getting
14:45
gonna get up and get some coffee and work on this more at a cafe
Avatar
how's this going?
15:00
It's gross
15:00
I wouldn't try to rev this at face value
Avatar
what's the hardest part
15:01
just super obfuscated?
15:02
The file were given is labeled O0
15:02
But looking at it, it's not like there's any computationally heavy loop
15:03
But it's obfuscated by many goto jumps and gadgets that I'm guessing will be fixed with higher optimization
15:05
im slow
15:05
but right now im going through it to make it compilable
15:05
since theres also heavy use of global vars
15:05
it also seeds the whole thing with the current time, (doesnt call rand, but the timestamp is used)
15:06
that part is easily patched/determined but its funky either way
Avatar
lol it calls srand but no rand? 🤣
15:09
it calls time(NULL) (edited)
15:09
and uses it in the password checker
15:09
¯\_(ツ)_/¯
Avatar
how tho? how can a random value help in checking a static flag?
Avatar
thats what i thought was odd
15:11
so i made a patched version with a static timestamp
15:11
but there's no timing attacks or anything interesting
15:11
whatever this program does, it uses then discards the timestamp
15:12
or uses the timestamp as an arbitrary gate to checking it
Avatar
okay I've been too afraid to download it until now, just gonna go for it. There go the next 6 hours of my life
15:12
Watch, we'll probably make like 1 breakthrough and then realize tbg already solved it lol
Avatar
i will entirely expect him to join, type ctf solve, and refuse to explain further
😂 2
Avatar
and we won't even be mad lolol
Avatar
when sigpwny did CSAW 2 years ago
15:14
we do it as a big hackathon with the club
15:14
kmh came in 8 hours late
15:14
(so like 1am)
15:15
solved all the pwns in 2 hours
15:15
then left
Avatar
okay, so just a C binary (not rust or go), dynamically linked, but stripped
Avatar
Avatar
Surg
solved all the pwns in 2 hours
🤣
Avatar
depends on if tbg has time, he didnt finish the 2nd one noobkekw
🙏 1
Avatar
I love it when that happens
Avatar
yeah, but look at like
15:15
a flow graph
15:15
its garbage
15:16
stripped is kinda irrelevant
15:16
theres only 1 function to care about
15:16
the first function called is a getinput func
15:16
then the 2nd one is the rev target
Avatar
hmm not even many functions
15:16
but big one at 0x124c
15:16
Looks like VM-style if I had to guess
15:17
bc big case statement
Avatar
except there's several of them
15:17
so my intuition for VM rev seems not there
15:17
i might be very wrong
Avatar
what does function 0x11c9 do?
15:19
oh that just reads in
Avatar
until newline
Avatar
gets 0x40 chars
15:20
and then back in main it copies first 7 chars over to another global
15:21
calls the big function
15:21
then writes from a initially empty buffer a 6 byte string then a different buffer a newline
Avatar
okay cool I think we're on the same page
Avatar
when the flag is wrong, the empty buffer gets filled with "Wrong!"
Avatar
yeah
15:21
time to rev big function 😭
15:21
sad how not sooooo big but sooooo hard
15:22
wtf is this 🤣
15:22
I pressed the graph button and it took like 10 seconds to load lol
Avatar
Like I said
15:23
It's cursed
15:23
It doesn't scream VM to me
15:23
At the same time it might be
Avatar
5 case statements
15:25
and yeah, if VM, instructions need to be somewhere
Avatar
64 byte programs would be horrifyingly small
Avatar
and I don't see any, only possibility is flag
15:26
and most "opcodes" would be outside printable range
15:26
ur right
15:26
okay
15:26
whats pv mean
Avatar
¯\_(ツ)_/¯
Avatar
each set of case statements have completely different gotos
15:30
so no overlapping/shared gotos
15:30
which means I think it should go pretty linearly, right?
15:30
altho the whole thing is wrapped in a while true loop so never know
15:32
i can work with that
15:32
thats down from 600
😂 1
15:35
uhh
15:35
i forget
15:35
if i just copy data as 8bit elements
15:35
do i need to worry about endianness
Avatar
idts?
15:36
oh 8 bit no
15:36
endianness is only on byte level
15:36
1 byte is the same little or big endian
15:37
but I think normally decompilers account for endianness so normally no
Avatar
im making the assumption that this code never indexes outside of arrays that binja detects
15:37
probably a bad one
Avatar
welp go off of that and if we're still stuck in 8 hours then we can revisit it 😂
Avatar
good point
Avatar
so many globalsss
Avatar
although we dont have 8 hours... (edited)
Avatar
yeah we do
Avatar
or do we
15:38
yeah
15:38
sorry
15:38
i can figure out time
Avatar
we've got like 16 more I think
15:40
i'm seeing 25 different globals
15:40
excluding the input global and timestamp one
Avatar
i have 22 defined in my rework code (incl input and timestamp)
15:48
im sure its fine
15:51
Okay it looks like there's one big case statement, and cases 1-5 each have a nested case statement
15:55
globals 0x7060 and 0x7066 (will) have the 2 messages printed out at the end
15:56
right before returning to the main function, the global that is printed (I've dubbed printed) is set to either 0x7060 or 0x7066 depending on a local variable. If that local variable is set to 0x2c0, it will be one (probably the "right" one), otherwise it'll be set to Wrong!. So I'm dubbing 0x7060 as right and 0x7066 as wrong
15:56
they have pre-initialized values, but probably get modified during execution
15:57
not just simple XOR tho
Avatar
i got the reimplementation to compile... but it isnt printing wrong! (edited)
👀 1
15:59
im going to have to do a bit of wizardry here
Avatar
Avatar
Legoclones
they have pre-initialized values, but probably get modified during execution
ohhhhhh no they're indexes
16:01
so there must be a large list of letters, and if the check value is 0x2c0, then the array right is selected. Each of the 6 elements in this array contains indexes to specific letters that spell out "Wrong!" or "Right!" (my guess)
16:01
16:02
it doesnt look any better...
😂 1
16:03
i was hoping that maybe that even if it wasnt working completely correct
16:03
that it would at least be simpler to look at
Avatar
Avatar
Legoclones
so there must be a large list of letters, and if the check value is 0x2c0, then the array right is selected. Each of the 6 elements in this array contains indexes to specific letters that spell out "Wrong!" or "Right!" (my guess)
okay, can confirm it will print Right! if it's right
Avatar
Avatar
Legoclones
okay, can confirm it will print Right! if it's right
just to confirm, you're talking about this block?
Avatar
yes
16:12
so our goal is to get that var to be 0x2c0 (edited)
Avatar
7066 holds an array of indexes to a whole thing of letters. Those indexes correspond to the letters printed
16:12
yeah
16:12
I've dubbed that var check_value
16:13
check_value is all based on the values stored at global 0x7374, but may have a non-0 starting value
16:13
if the indexes at another global of the iterator == 5, it will add a value from 0x7374 to check_value. Note the loop goes through 28 times (28 letters??)
16:19
pv pro got 2nd blood
Avatar
global 0x7360 is 784 chars long, or 112 7-byte segments long. I coped it out and parsed every 7th byte and got this array - [0x2, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0]
16:28
Those are just 4-byte ints
16:29
for this - [0x2, 0x3, 0x2, 0x4, 0x1, 0x1, 0x5, 0x1, 0x4, 0x4, 0x2, 0x2, 0x3, 0x2, 0x5, 0x4, 0x2, 0x1, 0x1, 0x3, 0x3, 0x1, 0x1, 0x4, 0x3, 0x1, 0x3, 0x4] (edited)
16:30
that means 0 <= check_itr <= 112
16:31
next byte after is 0x40, so maybe when check_itr == 113, it hits the default statement in the case switch, initializes check, and exits
16:31
those look like opcodes tho ngl 👀
16:37
assuming the instructions never get modified (only read), then the line check_value = check_value + (&DAT_00107374)[(ulong)check_itr * 7]; (where we want check_value == 0x2c0) should only be called twice, since there are 2x 0x5 instructions
Avatar
what a horrible vm
16:46
ok
16:50
alright yeah @Iyed i dont think reoptimizing is the play here
16:50
im going to try an assist lego as much as i can
Avatar
at those 2 times, check_itr should have values of 3 and 7. Assuming the constants at 0x7374 (which overlaps with 0x7360) also don't change, that means it'll add static value of 0 and 0 😦
16:51
i might've done something wrong
16:53
Each line number is a new "instruction" (each instruction is ulong, or 8 bytes). Since only every 7th instruction is used, I've marked each valid instruction with the #. It's in the format # index --> value, so # 1 --> 2 means instructions[1] == 0x2
16:53
and values from the 0x7374 global (dubbed value_list now) are added to check_value
16:54
however, they're only added if the instruction at that index is 0x5, of which are two, at indexes 0x3 and 0x7. The ulongs at these indexes tho are 0 😭
16:54
so either instructions are modified, value_list is modified, or I parsed wrong
16:56
Here is value_list (same principles for format)
2.75 KB
Avatar
so you made 0x7374 an array of ulongs, correct?
16:56
ignoring the overlap with 0x7360
Avatar
Avatar
Legoclones
Here is value_list (same principles for format)
yes, 0x7374 is here. An array of ulongs (and it so happens to overlap with 0x7360)
Avatar
Avatar
Legoclones
Each line number is a new "instruction" (each instruction is ulong, or 8 bytes). Since only every 7th instruction is used, I've marked each valid instruction with the #. It's in the format # index --> value, so # 1 --> 2 means instructions[1] == 0x2
this is the array of ulongs for 0x7360 (instructions) (edited)
16:58
what I don't understand is how 0x2c0 can be achieved by adding single-digit numbers
16:59
Because of endianness, there are some in value_list that are larger, but they're MUCH larger
Avatar
l/rshifts?
Avatar
hmm wait actually
16:59
the loop goes through 28 times, meaning there can be up to 28 indexes... I only have instructions and values up to 13
16:59
That may be why
17:00
So looks like these 2 may overlap with a whole bunch of other globals too
17:01
and if those globals are modified at runtime, then it's likely the values in instructions and value_list will change as time goes on
17:01
i mean it's possible they wont, but I wouldn't be surprised if they do
17:01
this just gets uglier and uglier 😂 😭
Avatar
vm opcodes determined by timestamp 💀
17:02
(i think those are different regions, but still)
Avatar
I think timestamp determines what instructions we start on
17:03
Avatar
just to confirm, your value_list is 95 entries long? or longer?
Avatar
Avatar
Surg
just to confirm, your value_list is 95 entries long? or longer?
95.5 🙂
Avatar
well i ignored the .5 cause
Avatar
Avatar
Legoclones
Click to see attachment 🖼️
check_itr is the instruction pointer lol
Avatar
¯\_(ツ)_/¯
Avatar
Avatar
Legoclones
check_itr is the instruction pointer lol
and initially set to timestamp stuff
17:04
so maybe only starts on correct instruction if started at the right time? Since some shifting, my guess is there's a window of time in which it will start on the correct instruction, and now is not in that window 😂 may need some brute force there too
17:04
but that's for later
Avatar
i mean we just patch the binary
17:05
the timestamp is just a obfuscation at this point
Avatar
yeah
Avatar
Did you determine global 7708? (edited)
Avatar
no, that's used as the value for the nested switch cases, right?
17:14
maybe sub-opcode?
17:14
looks like that one changes
17:14
i'm working on getting all 28 values for value_list and instructions through gdb rn
Avatar
[0x2, 0x2, 0x1, 0x5, 0x4, 0x2, 0x3, 0x5, 0x2, 0x1, 0x3, 0x1, 0x3, 0x3, 0x100000040, 0x0, 0x61, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0] is 29 long
17:17
now I'm even more positive those are gonna change later on
Avatar
Avatar
Legoclones
[0x2, 0x2, 0x1, 0x5, 0x4, 0x2, 0x3, 0x5, 0x2, 0x1, 0x3, 0x1, 0x3, 0x3, 0x100000040, 0x0, 0x61, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0] is 29 long
this is instructions
17:18
extends from 0x7360 to 0x7b7f
17:18
any globals in this range that are modified will affect these instructions
Avatar
Avatar
Legoclones
extends from 0x7360 to 0x7b7f
are you sure about that end addr?
Avatar
uhhh wait no
Avatar
Avatar
Surg
are you sure about that end addr?
0x7948 0x7980 I think (edited)
Avatar
Avatar
Surg
766c?
no, it's gonna be much higher if we're looking at 28 indexes of every 7th ulong
Avatar
PROGBITS ends at 0x767c
Avatar
does this start indexing out of .bss???
Avatar
28 * 7 * 8 = 1568 bytes (28 indexes * every 7th index * 8 bytes per ulong) = 0x620, 0x7360 + 0x620 = 0x7980
Avatar
but then like, whats at address 0x7770
17:28
unless im tripping
Avatar
hmmmm 🤔 yeah even .bss doesn't go past 0x7717
Avatar
like that would mean it indexes pass where any segment is defined
17:28
synthetic builtins end at 0x7790
Avatar
technically the segment is still there and valid until 0x8000
17:29
so it wouldn't segfault from reading...
17:29
just can't really place any data there
17:29
idk bro
17:30
what told you that value_list was ulongs
Avatar
i mean look at this. check_itr is casted to ulong, then every 7th, from 0 to 0x1c in increments of 1
Avatar
its not casted to long
17:31
check_itr is casted to long
17:31
instructions is who knows
17:31
wait
17:31
yes
Avatar
mine shows both casted
Avatar
your shows that somewhere, the output of instructions and value_list are ulongs
Avatar
okay honestly if this is intended, how do people even think of this stuff
17:32
i'm pretty sure that means check_itr is casted to ulong before * 7, but I might be wrong
Avatar
cause my confusion is at that only check_itr is casted to ulong, but thats just for indexing
17:32
yes, but thats an index
17:33
not the type of the array, right? (edited)
Avatar
also values make sense if u interpret it that way
17:33
well up until past 13 index 🤣
Avatar
thats the problem
Avatar
okay so what's an alternative
Avatar
well my first thought is what if everything is uint8_t?
Avatar
just regular int?
17:34
unsigned
17:34
4 bytes
Avatar
that or even 1 byte, since theres that unknown subopcode we havent dug into
17:35
it might very well just be taking this 1 byte at a time (edited)
Avatar
that would mean 0x7360 has (28 * 7 * 4 = ) 0x310 bytes, which goes to 0x7670 (edited)
Avatar
which is in line
Avatar
yeah, that's right before the section ends
Avatar
or specifically, it stops before a premade index variable
17:36
cause 0x7670 = 64
Avatar
okay wait looking at instructions that way
17:36
this also looks good hold up
17:36
lemme dump them
Avatar
ok confusion resolved
17:36
when you said the instructions ends at 0x7bXX i was like wait (edited)
Avatar
[0x2, 0x3, 0x2, 0x4, 0x1, 0x1, 0x5, 0x1, 0x4, 0x4, 0x2, 0x2, 0x3, 0x2, 0x5, 0x4, 0x2, 0x1, 0x1, 0x3, 0x3, 0x1, 0x1, 0x4, 0x3, 0x1, 0x3, 0x4, 0x40]
17:39
now lemme do the same for values
17:40
okay all values are 0 (assuming uint8_t)
17:40
maybe those get filled in later on based on runtime
17:40
that would make sense
17:41
also as a note of reference, the 2 0x5 instructions are offset 0x7408 and 0x74e8. So assuming no instructions are changed, then whatever values end up at those 2 offsets will be added to an undetermined starting number for check_value that we need to equal 0x2c0
17:48
okay, so we know where the final check is and where it gets numbers from, we have instructions and overall structure is 5 nested case statements. Time to investigate case statements to see what it modifies, and determine what other globals/vars are
17:50
0x7708 is a single integer. It's given a starting value, then as it goes through each instruction & corresponding operations, that "sub-opcode" changes. So it changes through all instructions, no pre-defined list. Changes on the fly
Avatar
my whole message was wrong lol
Avatar
Did you delete smth
17:57
Ok
Avatar
Ghidra shows 17 code segments inside case 0 for a nested case, but it's just weird. Each segment has a breaking condition that will cause it to not run other segments, even though they're sequential
17:58
maybe a possibility of running 2 segments, but that's the max (edited)
Avatar
Yeah, binary ninja does things a little weirdly, it doesn't show default cases, but all the control flow is there
Avatar
gotcha
17:59
this control flow is just so wack lol
18:02
okay wait I don't understand something
18:04
let's say we're entering the loop that interprets instructions for the first time. The instruction is 1 and the sub_opcode is 2. From what I'm seeing, it'll run like 3 lines, then skip to the bottom of the entire instruction loop and start over again? I haven't quite figured out what code at the top of the loop does, and what the code at the bottom of the loop does, but how does check_itr increase in that case??
18:07
Lemme write this out in psuedo C code do { // block of code where globals are initialized/changed switch(instructions) { default: // do final check and exit case 1: switch(sub_opcode) { // ... } case 2: case 3: case 4: case 5: } // end block of code where globals are changed } while (true); (edited)
18:08
all we've done so far is analyze the // do final check and exit line
18:08
and get instructions
18:09
Hold on
Avatar
Avatar
Legoclones
let's say we're entering the loop that interprets instructions for the first time. The instruction is 1 and the sub_opcode is 2. From what I'm seeing, it'll run like 3 lines, then skip to the bottom of the entire instruction loop and start over again? I haven't quite figured out what code at the top of the loop does, and what the code at the bottom of the loop does, but how does check_itr increase in that case??
are you saying that the first few instructions dont permit check iter to increase?
18:12
because remember that at the first block of globals we figured out
Avatar
check_iter is not touched anywhere inside the cases
Avatar
check_iter is initialized by the timestamp
Avatar
only in the beginning and end blocks
Avatar
but the timestamp is also modified
Avatar
okay yeah then that makes sense
Avatar
0000171d timeStamp = ((timeStamp * 0x41c64e6d) + 0x3039); 00001729 uint32_t rax_40 = (timeStamp >> 0x10); 00001743 check_itr = (rax_40 - (((int32_t)((((uint64_t)(rax_40 >> 2)) * 0x24924925) >> 0x20)) * 0x1c));
Avatar
because check_itr is REinitialized by the timestamp for each instruction
18:13
if timestamp never changed, the instruction would never change
18:14
so because the timestamp changes, check_itr changes, so the instructions progress
Avatar
but we have to be even more careful
18:14
thats not the only place timestamp changes
Avatar
Avatar
Legoclones
maybe those get filled in later on based on runtime
oooh also yes, value_list IS modified in the block of code at the end
18:17
frick
18:17
instructions are also modified during runtime 😭
18:17
this executable is dynamic as frick
Avatar
this vm builds a plane in the air then changes the model 3 times during flight
Avatar
bro frrr 🤣
Avatar
now im not a symbolic execution wizard
18:18
but would angr be useful at this point
Avatar
dude tbh no idea
Avatar
maybe if we consider this in terms of constraints of what we want the instruction and value list to be
18:19
erg
18:19
i guess that constraint is: the flag
Avatar
and the timestamp
18:19
what we control:
  • flag
  • timestamp What we want to see change:
  • value_list for check_value
Avatar
also unrelated pv = paravirtualization
18:22
probably not helpful but the closest ive seen for this name lol
Avatar
okay, here's the thing tho. check_itr will be initialized by the timestamp at the beginning block, it'll go through all the cases which will modify the other globals, then at the very end, it will modify only the instruction it just ran
Avatar
Avatar
Surg
also unrelated pv = paravirtualization
hmm that sounds right
Avatar
so unless theres a jump back
18:24
it wont affect future instructions in the flow
Avatar
correct
18:24
so if check_itr progresses linearly, it should be fine
18:24
the only possible use is it sets it to 0x5, because then the value associated with it is added to check_value at the end
Avatar
So my current mental model
18:25
is we have two lists
18:25
instructions, and values
18:25
if the op has a parameter, its in the corresponding index of values?
Avatar
no
18:28
if the op has a parameter, it's 0x7708 (I call it sub_opcode). This is a single integer, and changes between every instruction
18:28
value_list is never accessed inside the cases
18:28
it only has values set at the end block, and is used in the final check
Avatar
what's 76f8 to you
18:30
opcode?
Avatar
when an instruction/opcode is finished running, it will change the instruction it just ran to 0x76f8
Avatar
but that value is used initially for the first chain of if statements
Avatar
yeah it looks like in the first case statement (if instruction/opcode == 1), it may set the instruction after executing to another value (edited)
18:34
It also sets 0x76f8 at the beginning code block
Avatar
this is what binja is showing me at the start:
18:36
lies
18:36
ok
18:36
data_76f8 = ((int16_t)*(int32_t*)((((rdx_28 << 3) - rdx_28) << 2) + &data_7360));
18:37
my tiny brain is not meant for a VM built on an esolang
Avatar
Avatar
Legoclones
It also sets 0x76f8 at the beginning code block
wait this is kind of right
Avatar
it sets 0x76f8 to the CURRENT instruction (and uses it in the switch statement)
Avatar
is that what the fucked shifting and casting is doing?
Avatar
Which means, unless it hits those if statements at the end of case 1, the instruction will always be overwritten with the same thing
18:38
meaning it won't change
Avatar
Avatar
Surg
is that what the fucked shifting and casting is doing?
bro I got no idea what your decompiler is doing 🤣 (edited)
Avatar
ive fucked things up
18:39
raw ghidra hurts my soul cutter is nicer but is missing functionality ida costs my rent binja has hit this middle ground, but it does not ghidra things
Avatar
yeah 😂
18:42
i hear idas the best but so expensiveeeee
Avatar
ok but everything is saying 76f8 is a 16bit int
Avatar
lol mine says 16-bit/2-byte, but the next 2 bytes aren't used for anything
18:45
so it's probably defined as an int (32 bits), but is only functionally used as a short (16 bits)
Avatar
incredible
Avatar
what's incredible is I'm eating a raw loaf of banana bread rn
18:46
just bit straight into the fricking thing
18:47
go off, man
18:47
live your dream
18:47
dude its so good lol
18:47
never done this before
18:48
but i need brain food for this monster of a program
18:48
frick i shouldn't have looked at the calories lol
18:48
okay the worst part is
18:48
I feel like we're only maybe halfway into this thing
18:49
like we're understanding the big picture and beginning/end code pieces, but haven't even dug into the middle yet
18:53
okay, sub_opcode is set to 0x7364 + check_itr at the beginning each instruction, and 0x7364 + check_itr is set to sub_opcode at the end of each instruction
18:54
which means sub_opcode also has a list, just like the instructions. And after running an instruction, not only is the instruction overwritten, but the sub_opcode at the same index is ALSO overwritten (edited)
18:55
although I think sub_opcode gets overwritten by a new value almost 100% of the time, and the instruction is overwritten by a new value almost never
18:55
WAIT
18:56
how many lists are there?
18:56
We already know instructions and value_list overlap, now so does sub_opcode (edited)
18:56
Are there 7 lists overlapping with each other?? Is that why it always skips every 7th with different offsets?
18:56
that's evilllllll
Avatar
also I lied
19:02
value_list is accessed during the cases
Avatar
i thought so
Avatar
its set to check_value in my decompiler
19:02
might even be modified too
19:03
WAiT aNoTHeR seCONd
Avatar
waiting
Avatar
wait nevermind
19:07
i had 3 paragraphs written about a weird case then realized it can never happen
19:07
false alarm 😭
19:09
okay i'm renaming check_itr to itr and check_value to value since they're independent of the final check and the name is confusing
Avatar
kill me
😂 1
19:11
so i labed 0x7708 as the dSubopcode (edited)
19:11
because thats what we established that it gets to
19:11
i think
19:11
but im starting to think that it doesnt matter the more cases you go down
19:11
because each if statement saves the state of that "register", then depends on previous ones (edited)
Avatar
dsubopcode?
19:13
like sub_opcode
19:13
(just making sure we're referring to the same thing lol)
19:14
because variables get set during execution
19:14
but that refers to the one in the global segment
Avatar
oh gotcha
19:14
like the list of subopcodes
19:14
that constantly changes
Avatar
Avatar
Legoclones
if the op has a parameter, it's 0x7708 (I call it sub_opcode). This is a single integer, and changes between every instruction
this
19:15
and yes, i think
19:15
idk, the cases are doing this on my end:
19:15
it defines a variable that it will set to that data var
19:15
but only if it goes into the if statement
Avatar
oh hmm
Avatar
this gets confusing when it defines the variable
19:16
and that condition fails
Avatar
Ghidra is showing it as actual cases, like it says case and not an if statement
19:16
and then it sets 0x7708 to a static value
19:17
like here you can see the case statements with a goto. The gotos are what I call "segments", and the sub_opcode is set to a static number lol
Avatar
im disassociating for a bit trying to figure out how this is the same code rq
Avatar
i have the static assignments, just not at the top level
19:19
idk
19:19
im sure its fine
Avatar
yeah lol the entire middle of my code is just case statements with a bunch of gotos
19:19
almost no if statements lololol
19:20
absolute pain
Avatar
Avatar
Legoclones
bro I got no idea what your decompiler is doing 🤣 (edited)
cause you responded to this for that shifting thing
19:20
but if you look at the disasm (edited)
19:20
it does do shl shifts
19:20
on the addresses
19:20
and subtracts rax
19:20
so binja is telling me that its doing exactly that
19:20
but ghidra sees it differently???
19:21
anyway, pedantics
Avatar
Avatar
Legoclones
yeah lol the entire middle of my code is just case statements with a bunch of gotos
yeah i have it too
19:21
dw we're there
Avatar
Avatar
Surg
it does do shl shifts
oh this is in the code block at the beginning of the loop
19:22
or the global assignment block
Avatar
yeah the shifts is its way of setting each global variable at the beginning or something
Avatar
to be specific
19:22
weird
Avatar
I can see it in the disasm but it doesn't show like that in decomp
19:22
idk
19:22
anyway
19:23
whats the next step
Avatar
i feel like we should start carving out the goto blocks
19:23
because it seems like the natural handler for each operation
19:23
but i feel like we don't have the best grasp on the register + memory stack yet
Avatar
yeah
Avatar
too many things are acting like an instruction pointer
😂 1
Avatar
maybe looking at gotos will help us understand how the other global variables and local variables fit in with it
Avatar
i need a mental break, im gonna come back to this in a half hour
Avatar
lol literally about to say the same thing
19:24
ping me when u do
19:24
gonna take a quick nap or something
19:24
will set an alarm too
Avatar
i just need to stare at not goto statements
Avatar
ok @Legoclones
00:58
im so lost
Avatar
Welp
02:10
That was a long nap
Avatar
thebadgod not playing? 😭
Avatar
Ig not. I will be able to hop on in like 2 hours, but the CTF will be mostly over by then
06:22
Are we still tracking to win?
Avatar
no idea, 6 chals lead is safe or not
06:27
4 hrs right
06:27
maybe good
Avatar
Who's fredd fan club
Avatar
Avatar
Legoclones
bro I got no idea what your decompiler is doing 🤣 (edited)
so i went to binja people to discuss this, they're fixing it on the next patch, and it's already up on dev:
Avatar
Daaang nice dude!!
Exported 494 message(s)